import FSPScopeHandling as scope
from Exceptions import UndefinedNameException, LabelsMismatchException

class FSPLocalProcess(dict):
  def __init__(self, localProcessId, fatherProcessNamespace):
    self.localProcessId = localProcessId
    self.namespace = scope.namespace.FSPNamespace(name=self.localProcessId, father=fatherProcessNamespace)
    self.namespace.addObject("", 0, "__local_process__", self, "local_process")


class FSPProcess(object):
  def __init__(self, processId, fatherNamespace=scope.globalNamespace):
    self.processId = processId
    self.alphabet = set()
    self.localProcessesTable = dict()
    self.namespace = scope.namespace.FSPNamespace(name=self.processId, father=fatherNamespace)
    self.namespace.addObject("", 0, "__process__", self, "process")
  @staticmethod
  def _processReference(s, l):
    process = None
    try:
      process = scope.currentNamespace.getName(s, l, "__process__", "process")
    except UndefinedNameException: pass
    return process
  @classmethod
  def expandCurrentAlphabet(cls, s, l, t):
    process = FSPProcess._processReference(s,l)
    if process:
      for label in t: process.alphabet.add(label)
    return t
  @classmethod
  def relabelCurrentAlphabet(cls, s, l, oldLabels, newLabels):
    process = FSPProcess._processReference(s, l)
    if process:
      if len(oldLabels) != len(newLabels):
        raise LabelsMismatchException(s, l)
      process.alphabet.difference_update(oldLabels)
      process.alphabet.update(newLabels)
    return newLabels
  @classmethod
  def hideCurrentAlphabet(cls, s, l, labelSet, mode):
    process = FSPProcess._processReference(s, l)
    if process:
      if mode == "hide":
        process.alphabet.difference_update(labelSet)
      elif mode == "expose":
        process.alphabet.intersection_update(labelSet)
      else:
        raise Exception()
    return labelSet
  @classmethod
  def addLocalProcess(cls, s, l, localProcessId):
    process = FSPProcess._processReference(s, l)
    if process:
      if not process.localProcessTable[localProcessId]:
        process.localProcessTable[localProcessId] = FSPLocalProcess(localProcessId, process.namespace)
      return process.localProcessTable[localProcessId]
    else:
      raise Exception()
  @classmethod
  def currentProcessId(cls, s, l):
    process = FSPProcess._processReference(s, l)
    if process: return process.processId

scope.namespace.typeMappings["process"] = FSPProcess
scope.namespace.typeMappings["incomplete_process"] = type(None)
scope.namespace.typeMappings["local_process"] = FSPLocalProcess
scope.namespace.typeMappings["incomplete_local_process"] = type(None)
